package net.yla.smartbus.core.base;

import com.vip.vjtools.vjkit.collection.ListUtil;
import lombok.Data;
import net.yla.smartbus.core.api.RequestCode;
import net.yla.smartbus.core.constants.CacheKeyPrefix;
import net.yla.smartbus.core.constants.YesNoEnum;
import net.yla.smartbus.core.exception.AppException;
import net.yla.smartbus.core.exception.DaoSetDefaultValueException;
import net.yla.smartbus.core.util.RedisUtil;
import net.yla.smartbus.core.util.StringEx;
import net.yla.smartbus.module.ba.right.login.dto.UserSession;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.reflections.Reflections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Service基类
 *
 * @author ZengXiaoLiang
 */
public class BaseService {

    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private RedisUtil redisUtil;
//    @Autowired
//	private LoginService loginService;

    /**
     * 新增记录时,设置通用的字段值:创建时间,默认删除状态否,uuid
     */
    protected <T> void insertSetDefaultValue(T obj) {

        List<Field> fields = getFields(obj);
        try {
            for (Field field : fields) {
                String name = field.getName();
                if (!"class".equals(name) && !Modifier.isStatic(field.getModifiers())) {
                    field.setAccessible(true);
                    if ("creationTime".equals(name)) {
                        field.set(obj, new Date());
                    }
                    if ("lastModifyTime".equals(name)) {
                        field.set(obj, new Date());
                    }
//                    if ("uuid".equals(name)) {
//                        field.set(obj, StringEx.newUUID());
//                    }
                    if ("flags".equals(name)) {
                        field.set(obj, YesNoEnum.N);
                    }
                }
            }
        } catch (Exception e) {
            throw new DaoSetDefaultValueException("设置默认值错误:" + e.toString(), RequestCode.server_error);
        }
    }

    /**
     * 新增记录时,设置通用的字段值:创建时间,默认删除状态否,uuid
     */
    protected <T> void insertSetDefaultValue(T obj, Integer userId) {

        List<Field> fields = getFields(obj);
        try {
            for (Field field : fields) {
                String name = field.getName();
                if (!"class".equals(name) && !Modifier.isStatic(field.getModifiers())) {
                    field.setAccessible(true);
                    if ("createDate".equals(name)) {
                        field.set(obj, new Date());
                    }
                    if ("updateDate".equals(name)) {
                        field.set(obj, new Date());
                    }
                    if ("delFlag".equals(name)) {
                        field.set(obj, YesNoEnum.N);
                    }
                    if ("createBy".equals(name)) {
                        field.set(obj, userId);
                    }
                    if ("updateBy".equals(name)) {
                        field.set(obj, userId);
                    }
                    if ("createTime".equals(name)) {
                        field.set(obj, new Date());
                    }
                    if ("updateTime".equals(name)) {
                        field.set(obj, new Date());
                    }
                }
            }
        } catch (Exception e) {
            throw new DaoSetDefaultValueException("设置默认值错误:" + e.toString(), RequestCode.server_error);
        }
    }

    private <T> List<Field> getFields(T obj) {
        List<Field> fields = new ArrayList<>();
        Class tempClass = obj.getClass();
        while (tempClass != null) {
            fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            tempClass = tempClass.getSuperclass();
        }
        return fields;
    }

    /**
     * 更新记录时,设置通用的更新字段:更新时间
     */
    protected <T> void updateSetDefaultValue(T obj) {
        List<Field> fields = getFields(obj);
        try {
            for (Field field : fields) {
                String name = field.getName();
                if (!"class".equals(name) && !Modifier.isStatic(field.getModifiers())) {
                    field.setAccessible(true);
                    if ("lastModifyTime".equals(name)) {
                        field.set(obj, LocalDateTime.now());
                    }
                }
            }
        } catch (Exception e) {
            throw new DaoSetDefaultValueException("设置默认值错误:" + e.toString(), RequestCode.server_error);
        }
    }

    /**
     * 更新记录时,设置通用的更新字段:更新时间,更新人
     */
    protected <T> void updateSetDefaultValue(T obj, Integer userId) {
        List<Field> fields = getFields(obj);
        try {
            for (Field field : fields) {
                String name = field.getName();
                if (!"class".equals(name) && !Modifier.isStatic(field.getModifiers())) {
                    field.setAccessible(true);
                    if ("updateDate".equals(name)) {
                        field.set(obj, new Date());
                    }
                    if ("lastModifyTime".equals(name)) {
                        field.set(obj, new Date());
                    }
                    if ("updateBy".equals(name)) {
                        field.set(obj, userId);
                    }
                }
            }
        } catch (Exception e) {
            throw new DaoSetDefaultValueException("设置默认值错误:" + e.toString(), RequestCode.server_error);
        }
    }

    /**
     * 通过枚举名称查询全部，新加个全部类型的
     *
     * @param enumName
     * @return
     * @throws Exception
     */
    protected List<EnumDTOTotal> getListWithTotalByEnumName(@PathVariable String enumName) throws Exception {
        List<EnumDTOTotal> retList = this.getListByEnumName(enumName);
        retList.add(0, new EnumDTOTotal(-1, "全部", "TOTAL"));
        return retList;
    }

    /**
     * 通过枚举名称查询全部，新加个全部类型的
     *
     * @param enumName
     * @return
     * @throws Exception
     */
    protected List<EnumDTOTotal> getListByEnumName(@PathVariable String enumName) throws Exception {
        List<EnumDTOTotal> retList = (List<EnumDTOTotal>) redisUtil.get(
                CacheKeyPrefix.SYS_ENUM_NAME_WITH_TOTAL + enumName);
//		List<EnumDTOTotal> retList = new ArrayList<>();
        if (retList == null) {
            Reflections reflections = new Reflections("net.seocoo.smartbus");
            Set<Class<? extends Enum>> allEnums = reflections.getSubTypesOf(Enum.class);
            if (allEnums != null && allEnums.size() > 0) {
                Class<? extends Enum> enumClass = null;
                //循环查询所有枚举类型class
                for (Class<? extends Enum> clazz : allEnums) {
                    if (StringUtils.equals(clazz.getSimpleName(), enumName)) {
                        enumClass = clazz;
                        break;
                    }
                }
                if (enumClass == null) {
                    throw new AppException(enumName + "未查到枚举类型", RequestCode.server_error);
                }

                Enum[] enumConstants = enumClass.getEnumConstants();
                if (enumConstants != null && enumConstants.length > 0) {
                    retList = new ArrayList<>();
                    for (int i = 0; i < enumConstants.length; i++) {
                        EnumDTOTotal enumDTO = new EnumDTOTotal();
                        Class<?> sub = enumConstants[i].getClass();
                        Method descMethod = sub.getDeclaredMethod("getDesc");
                        Method codeMethod = sub.getDeclaredMethod("getCode");
                        String desc = (String) descMethod.invoke(enumConstants[i]);
                        Integer code = (Integer) codeMethod.invoke(enumConstants[i]);
                        enumDTO.setCode(code);
                        enumDTO.setDesc(desc);
                        enumDTO.setName(enumConstants[i].name());
                        retList.add(enumDTO);
                    }
                    if (retList.size() > 0) {
                        redisUtil.set(CacheKeyPrefix.SYS_ENUM_NAME_WITH_TOTAL + enumName, retList, 7L, TimeUnit.DAYS);
                    }
                }
            }
        }
        return retList;
    }

    @Data
    public class EnumDTOTotal {
        private Integer code;
        private String desc;
        private String name;

        public EnumDTOTotal(Integer code, String desc, String name) {
            this.code = code;
            this.desc = desc;
            this.name = name;
        }

        public EnumDTOTotal() {
        }
    }

    /**
     * 通过token查询登录用户信息
     */
    public UserSession getUserSession(String token) {
        if (StringUtils.isEmpty(token)) {
//			throw new AppException("用户未登陆", RequestCode.Not_Login);
        }
        UserSession dto = (UserSession) redisUtil.get(token);
        if (dto == null) {
            throw new AppException("用户未登陆", RequestCode.Not_Login);
        }
        return dto;
    }

    protected String orderBy(List<String> sort, List<Integer> order) {
        if (ListUtil.isNotEmpty(sort) && ListUtil.isNotEmpty(order) && sort.size() == order.size()) {
            for (int i = 0; i < sort.size(); i++) {
                Integer temp = order.get(i);
                String order1 = getOrder(temp);
                sort.set(i, sort.get(i) + order1);
            }
            return StringUtils.join(sort, ",");
        }
        return "";
    }

    private String getOrder(int temp) {
        if (temp == 0 || temp == 1) {
            return "";
        } else {
            return " desc";
        }
    }

    /**
     * 移除fields中在cl中没有的字段名
     *
     * @param cl
     * @param fields TODO 当前只适用于单表查询 可扩展为多表查询
     */
    public List<String> selected(Class cl, List<String> fields) {
        // 获取实体字段
        List<String> fields1 = this.getModifierOf2(cl);
        // 取交集
        List<String> result = this.intersect(fields1, fields);
        return result;

    }

    private List<String> getModifierOf2(Class cl) {
        List<String> fields = new ArrayList<>();
        if (cl != null) {
            Field[] fields1 = cl.getDeclaredFields();
            for (Field declaredField : fields1) {
                if (declaredField.getModifiers() == 2) {
                    fields.add(declaredField.getName());
                }
            }
        }
        return fields;
    }

    public List<String> intersect(List<String> arr1, List<String> arr2) {
        Map<String, Boolean> map = new HashMap<>();
        LinkedList<String> list = new LinkedList<>();
        for (String str : arr1) {
            if (!map.containsKey(str)) {
                map.put(str, Boolean.FALSE);
            }
        }
        for (String str : arr2) {
            if (map.containsKey(str)) {
                map.put(str, Boolean.TRUE);
            }
        }

        for (Map.Entry<String, Boolean> e : map.entrySet()) {
            if (e.getValue().equals(Boolean.TRUE)) {
                list.add(e.getKey());
            }
        }

        return list;
    }
}
